<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="description" content="杰克小麻雀的博客" />
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
    彻底弄懂Python标准库源码（二）—— os模块（续） |  半亩方塘
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

</head>

</html>
<body>
  <div id="app">
    
      
      <canvas width="1777" height="841"
        style="position: fixed; left: 0px; top: 0px; z-index: 99999; pointer-events: none;"></canvas>
      
    <main class="content on">
      <section class="outer">
  <article
  id="post-彻底弄懂Python标准库源码（二）—— os模块（续）"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h1 class="article-title sea-center" style="border-left:0" itemprop="name">
  彻底弄懂Python标准库源码（二）—— os模块（续）
</h1>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/09/09/%E5%BD%BB%E5%BA%95%E5%BC%84%E6%87%82Python%E6%A0%87%E5%87%86%E5%BA%93%E6%BA%90%E7%A0%81%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20os%E6%A8%A1%E5%9D%97%EF%BC%88%E7%BB%AD%EF%BC%89/" class="article-date">
  <time datetime="2020-09-10T00:29:24.000Z" itemprop="datePublished">2020-09-09</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/Python/">Python</a>
  </div>
  
<div class="word_count">
    <span class="post-time">
        <span class="post-meta-item-icon">
            <i class="ri-quill-pen-line"></i>
            <span class="post-meta-item-text"> 字数统计:</span>
            <span class="post-count">8.9k</span>
        </span>
    </span>

    <span class="post-time">
        &nbsp; | &nbsp;
        <span class="post-meta-item-icon">
            <i class="ri-book-open-line"></i>
            <span class="post-meta-item-text"> 阅读时长≈</span>
            <span class="post-count">41 分钟</span>
        </span>
    </span>
</div>
 
    </div>
      
    <div class="tocbot"></div>




  
    <div class="article-entry" itemprop="articleBody">
       
  <blockquote>
<p>作者: 杰克小麻雀<br>原文链接: <a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/108492310">https://blog.csdn.net/yushuaigee/article/details/108492310</a></p>
</blockquote>
<p>本文续 <a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/106755148">彻底弄懂Python标准库源码（一）—— os模块</a></p>
<p>本文所用 Python3.8.3 标准库 os.py文件, 可以在CPython<a target="_blank" rel="noopener" href="https://github.com/python/cpython/blob/3.8/Lib/os.py">官方GitHub地址</a>下载 。</p>
<p><strong>目录</strong></p>
<ul>
<li><p><a href="#%E7%AC%AC423~529%E8%A1%8C%20fwalk%E2%80%94%E2%80%94%E7%9B%AE%E5%BD%95%E6%A0%91%E7%94%9F%E6%88%90%E5%99%A8">第423~529行 fwalk——目录树生成器</a></p>
</li>
<li><p><a href="#%E7%AC%AC531~654%E8%A1%8C%20exec%E5%87%BD%E6%95%B0%E6%97%8F">第531~654行 exec函数族</a></p>
</li>
<li><p><a href="#%E7%AC%AC657~713%E8%A1%8C%20_Environ%E2%80%94%E2%80%94%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E4%BF%A1%E6%81%AF%E7%9A%84%E7%B1%BB">第657~713行 _Environ——环境变量信息的类</a></p>
</li>
<li><p><a href="#%E7%AC%AC715~721%E8%A1%8C%20os.putenv%E2%80%94%E2%80%94%E8%AE%BE%E7%BD%AE%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F">第715~721行 os.putenv——设置环境变量</a></p>
</li>
<li><p><a href="#%E7%AC%AC723~729%E8%A1%8C%20os.unsetenv%E2%80%94%E2%80%94%E5%88%A0%E9%99%A4%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F">第723~729行 os.unsetenv——删除环境变量</a></p>
</li>
<li><p><a href="#%E7%AC%AC731~759%E8%A1%8C%20os.environ%E2%80%94%E2%80%94%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E4%BF%A1%E6%81%AF">第731~759行 os.environ——环境变量信息</a></p>
</li>
<li><p><a href="#%E7%AC%AC766~770%E8%A1%8C%20getenv%E2%80%94%E2%80%94%E8%8E%B7%E5%8F%96%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F">第766~770行 getenv——获取环境变量</a></p>
</li>
<li><p><a href="#%E7%AC%AC772~794%E8%A1%8C%20environb%EF%BC%8Cgetenvb%E2%80%94%E2%80%94%E5%AD%97%E8%8A%82%E5%9E%8B(byte)%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F">第772~794行 environb，getenvb——字节型(byte)环境变量</a></p>
</li>
<li><p><a href="#%E7%AC%AC796~827%E8%A1%8C%20fsencode%EF%BC%8Cfsdecode%E2%80%94%E2%80%94%E8%B7%AF%E5%BE%84%E5%90%8D%E7%9A%84%E7%BC%96%E7%A0%81%E5%92%8C%E8%A7%A3%E7%A0%81">第796~827行 fsencode，fsdecode——路径名的编码和解码</a></p>
</li>
<li><p><a href="#%E7%AC%AC829~970%E8%A1%8C%20spawn%E5%87%BD%E6%95%B0%E6%97%8F">第829~970行 spawn函数族</a></p>
</li>
<li><p><a href="#%E7%AC%AC973~1016%E8%A1%8C%20popen%E2%80%94%E2%80%94%E6%89%A7%E8%A1%8Ccmd%E6%88%96%E8%80%85shell%E5%91%BD%E4%BB%A4">第973~1016行 popen——执行cmd或者shell命令</a></p>
</li>
<li><p><a href="#%E7%AC%AC1018~1023%E8%A1%8C%20fdopen%E2%80%94%E2%80%94%E6%89%93%E5%BC%80%E6%96%87%E4%BB%B6">第1018~1023行 fdopen——打开文件</a></p>
</li>
<li><p><a href="#%E7%AC%AC1026~1061%E8%A1%8C%20fspath%E2%80%94%E2%80%94%E8%B7%AF%E5%BE%84%E6%A0%87%E5%87%86%E5%8C%96">第1026~1061行 fspath——路径标准化</a></p>
</li>
<li><p><a href="#%E7%AC%AC1064~1077%E8%A1%8C%20PathLike%E2%80%94%E2%80%94%E4%BB%80%E4%B9%88%E6%98%AFPathLike">第1064~1077行 PathLike——什么是PathLike</a></p>
</li>
<li><p><a href="#%E7%AC%AC1080~1115%E8%A1%8C%20add_dll_directory%E2%80%94%E2%80%94%20%E6%B7%BB%E5%8A%A0DLL%E6%96%87%E4%BB%B6%E6%90%9C%E7%B4%A2%E8%B7%AF%E5%BE%84">第1080~1115行 add_dll_directory—— 添加DLL文件搜索路径</a></p>
</li>
<li><p><a href="#%E6%80%BB%E7%BB%93">总结</a></p>
</li>
<li><p><a href="#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5">参考链接</a></p>
</li>
</ul>
<h3 id="第423-529行-fwalk——目录树生成器"><a href="#第423-529行-fwalk——目录树生成器" class="headerlink" title="第423~529行 fwalk——目录树生成器"></a>第423~529行 fwalk——目录树生成器</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> &#123;<span class="built_in">open</span>, stat&#125; &lt;= supports_dir_fd <span class="keyword">and</span> &#123;scandir, stat&#125; &lt;= supports_fd:</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">fwalk</span>(<span class="params">top=<span class="string">&quot;.&quot;</span>, topdown=<span class="literal">True</span>, onerror=<span class="literal">None</span>, *, follow_symlinks=<span class="literal">False</span>, dir_fd=<span class="literal">None</span></span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Directory tree generator.</span></span><br><span class="line"><span class="string">        This behaves exactly like walk(), except that it yields a 4-tuple</span></span><br><span class="line"><span class="string">            dirpath, dirnames, filenames, dirfd</span></span><br><span class="line"><span class="string">        `dirpath`, `dirnames` and `filenames` are identical to walk() output,</span></span><br><span class="line"><span class="string">        and `dirfd` is a file descriptor referring to the directory `dirpath`.</span></span><br><span class="line"><span class="string">        The advantage of fwalk() over walk() is that it&#x27;s safe against symlink</span></span><br><span class="line"><span class="string">        races (when follow_symlinks is False).</span></span><br><span class="line"><span class="string">        If dir_fd is not None, it should be a file descriptor open to a directory,</span></span><br><span class="line"><span class="string">          and top should be relative; top will then be relative to that directory.</span></span><br><span class="line"><span class="string">          (dir_fd is always supported for fwalk.)</span></span><br><span class="line"><span class="string">        Caution:</span></span><br><span class="line"><span class="string">        Since fwalk() yields file descriptors, those are only valid until the</span></span><br><span class="line"><span class="string">        next iteration step, so you should dup() them if you want to keep them</span></span><br><span class="line"><span class="string">        for a longer period.</span></span><br><span class="line"><span class="string">        Example:</span></span><br><span class="line"><span class="string">        import os</span></span><br><span class="line"><span class="string">        for root, dirs, files, rootfd in os.fwalk(&#x27;python/Lib/email&#x27;):</span></span><br><span class="line"><span class="string">            print(root, &quot;consumes&quot;, end=&quot;&quot;)</span></span><br><span class="line"><span class="string">            print(sum(os.stat(name, dir_fd=rootfd).st_size for name in files),</span></span><br><span class="line"><span class="string">                  end=&quot;&quot;)</span></span><br><span class="line"><span class="string">            print(&quot;bytes in&quot;, len(files), &quot;non-directory files&quot;)</span></span><br><span class="line"><span class="string">            if &#x27;CVS&#x27; in dirs:</span></span><br><span class="line"><span class="string">                dirs.remove(&#x27;CVS&#x27;)  # don&#x27;t visit CVS directories</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(top, <span class="built_in">int</span>) <span class="keyword">or</span> <span class="keyword">not</span> <span class="built_in">hasattr</span>(top, <span class="string">&#x27;__index__&#x27;</span>):</span><br><span class="line">            top = fspath(top)</span><br><span class="line">        <span class="comment"># Note: To guard against symlink races, we use the standard</span></span><br><span class="line">        <span class="comment"># lstat()/open()/fstat() trick.</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> follow_symlinks:</span><br><span class="line">            orig_st = stat(top, follow_symlinks=<span class="literal">False</span>, dir_fd=dir_fd)</span><br><span class="line">        topfd = <span class="built_in">open</span>(top, O_RDONLY, dir_fd=dir_fd)</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">if</span> (follow_symlinks <span class="keyword">or</span> (st.S_ISDIR(orig_st.st_mode) <span class="keyword">and</span></span><br><span class="line">                                    path.samestat(orig_st, stat(topfd)))):</span><br><span class="line">                <span class="keyword">yield</span> <span class="keyword">from</span> _fwalk(topfd, top, <span class="built_in">isinstance</span>(top, <span class="built_in">bytes</span>),</span><br><span class="line">                                  topdown, onerror, follow_symlinks)</span><br><span class="line">        <span class="keyword">finally</span>:</span><br><span class="line">            close(topfd)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_fwalk</span>(<span class="params">topfd, toppath, isbytes, topdown, onerror, follow_symlinks</span>):</span></span><br><span class="line">        <span class="comment"># Note: This uses O(depth of the directory tree) file descriptors: if</span></span><br><span class="line">        <span class="comment"># necessary, it can be adapted to only require O(1) FDs, see issue</span></span><br><span class="line">        <span class="comment"># #13734.</span></span><br><span class="line"> </span><br><span class="line">        scandir_it = scandir(topfd)</span><br><span class="line">        dirs = []</span><br><span class="line">        nondirs = []</span><br><span class="line">        entries = <span class="literal">None</span> <span class="keyword">if</span> topdown <span class="keyword">or</span> follow_symlinks <span class="keyword">else</span> []</span><br><span class="line">        <span class="keyword">for</span> entry <span class="keyword">in</span> scandir_it:</span><br><span class="line">            name = entry.name</span><br><span class="line">            <span class="keyword">if</span> isbytes:</span><br><span class="line">                name = fsencode(name)</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="keyword">if</span> entry.is_dir():</span><br><span class="line">                    dirs.append(name)</span><br><span class="line">                    <span class="keyword">if</span> entries <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                        entries.append(entry)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    nondirs.append(name)</span><br><span class="line">            <span class="keyword">except</span> OSError:</span><br><span class="line">                <span class="keyword">try</span>:</span><br><span class="line">                    <span class="comment"># Add dangling symlinks, ignore disappeared files</span></span><br><span class="line">                    <span class="keyword">if</span> entry.is_symlink():</span><br><span class="line">                        nondirs.append(name)</span><br><span class="line">                <span class="keyword">except</span> OSError:</span><br><span class="line">                    <span class="keyword">pass</span></span><br><span class="line"> </span><br><span class="line">        <span class="keyword">if</span> topdown:</span><br><span class="line">            <span class="keyword">yield</span> toppath, dirs, nondirs, topfd</span><br><span class="line"> </span><br><span class="line">        <span class="keyword">for</span> name <span class="keyword">in</span> dirs <span class="keyword">if</span> entries <span class="keyword">is</span> <span class="literal">None</span> <span class="keyword">else</span> <span class="built_in">zip</span>(dirs, entries):</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> follow_symlinks:</span><br><span class="line">                    <span class="keyword">if</span> topdown:</span><br><span class="line">                        orig_st = stat(name, dir_fd=topfd, follow_symlinks=<span class="literal">False</span>)</span><br><span class="line">                    <span class="keyword">else</span>:</span><br><span class="line">                        <span class="keyword">assert</span> entries <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span></span><br><span class="line">                        name, entry = name</span><br><span class="line">                        orig_st = entry.stat(follow_symlinks=<span class="literal">False</span>)</span><br><span class="line">                dirfd = <span class="built_in">open</span>(name, O_RDONLY, dir_fd=topfd)</span><br><span class="line">            <span class="keyword">except</span> OSError <span class="keyword">as</span> err:</span><br><span class="line">                <span class="keyword">if</span> onerror <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                    onerror(err)</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="keyword">if</span> follow_symlinks <span class="keyword">or</span> path.samestat(orig_st, stat(dirfd)):</span><br><span class="line">                    dirpath = path.join(toppath, name)</span><br><span class="line">                    <span class="keyword">yield</span> <span class="keyword">from</span> _fwalk(dirfd, dirpath, isbytes,</span><br><span class="line">                                      topdown, onerror, follow_symlinks)</span><br><span class="line">            <span class="keyword">finally</span>:</span><br><span class="line">                close(dirfd)</span><br><span class="line"> </span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> topdown:</span><br><span class="line">            <span class="keyword">yield</span> toppath, dirs, nondirs, topfd</span><br><span class="line"> </span><br><span class="line">    __all__.append(<span class="string">&quot;fwalk&quot;</span>)</span><br></pre></td></tr></table></figure>
<p>第423行 {open, stat} 是定义一个集合 (set)，判断这个集合在 supports_dir_fd 这个集合里并且{scandir, stat} 在 supports_fd 这个集合里，才会定义 fwalk 函数。那么什么情况下判断才会成立呢? 先看一下 supports_dir_fd 和supports_fd是什么意思，这两个集合是在前面123行和143行定义的。</p>
<p>fd在Linux中一般指 file_descriptor 文件描述符。Linux为了实现一切皆文件的设计哲学，不仅将数据抽象成了文件，也将一切操作和资源抽象成了文件，比如说硬件设备，socket，磁盘，进程，线程等。在操作这些所谓的文件的时候，我们每操作一次就找一次名字会耗费大量的时间和效率。所以每一个文件对应一个索引，这样要操作文件的时候，我们直接找到索引就可以对其进行操作了。我们将这个索引叫做文件描述符（file descriptor），简称fd，在系统里面是一个非负的整数。每打开或创建一个文件，内核就会向进程返回一个fd，第一个打开文件是0,第二个是1，依次递增。（关于文件描述符详细理解，可以参考我的另一篇博文—— <a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/107883964">彻底弄懂 Linux 下的文件描述符</a>）</p>
<p>我理解在这里也类似，知道两个集合命名中 fd 的意思有助于理解这两个集合的含义和后面 fwalk 函数最后一个参数的含义。 不同的系统都会支持对文件目录、文件进行一些操作，比如chmod，mkdir这些。这些操作会在Python解释器启动的时候被加载在builtins的globals()字典里变成了内置方法，chmod，mkdir 这些字符串作为键，对应的具体的操作函数作为值。通过102行的_add函数处理进行过滤，只留下当前系统所支持的那些，supports_dir_fd 里面放的就是对文件目录进行操作的函数，supports_fd里面放的就是文件进行操作的函数。过滤过程是根据chmod，mkdir这些键来过滤的，类似根据索引来找具体方法一样。总之，这一行的判断其实可以看做是为了识别在不同的系统中是否定义fwalk 方法，这里经过测试，windows下判断不通过，Linux下判断通过。也就是说os模块，在Linux下有 fwalk 方法，而在Wndows下没有 fwalk 方法。</p>
<p>第425行，可以看出 fwalk 这个函数和前面的 walk 类似，但是多了一个参数 dir_fd=None（参数入参中的那个星号 * 的意思可以参考我的另一篇博客——<a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/107567001">彻底弄懂 Python3中入参里的*号的作用</a>）。</p>
<p>注释是说：函数作用是目录树生成器。</p>
<p>这个函数除了返回值是一个四元组(dirpath, dirnames, filenames, dirfd)之外，其他实现情况和 walk() 是完全相同的。前三个参数的意义和 walk() 也相同，最后一个参数 dirfd 是目录 dirpath 的文件描述符。</p>
<p>fwalk() 比walk() 的优点是，当可选参数 follow_symlinks is False 时，这个函数对于 symlink race 漏洞是安全的。(symlink race，符号链接竞争漏洞，见 <a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Symlink_race">Symlink race—-Wikipedia</a>)。如果 dir_fd 参数不是 None，它应该是指向某个目录的文件描述符，并且 top 参数也应该对应修改，改为相对于该目录的相对路径。(fwalk始终支持dir_fd)。</p>
<p>注意：由于fwalk()产生文件描述符，这些描述符仅在下一个迭代步骤之前有效，所以如果要想保留更长的时间应该用dup()函数处理它们。(dup是一个复制文件描述符的系统函数)。</p>
<p>第458~459行，将 top 转化为标准字符串目录。</p>
<p>第460<del>462行，当 follow_syslinks 为False时，调用系统函数 stat 获取 top 的基本信息。然后调用系统函数 open 获取 top 的文件描述符。(关于 stat 函数见 <a target="_blank" rel="noopener" href="https://blog.csdn.net/u011150719/article/details/50865280">linux stat函数（获取文件详细信息）</a>)。通过这些处理能防止 symlink races 漏洞，具体原因见下面 43</del>49 行。</p>
<p>第465~471行，当follow_syslinks 为True时，直接调用 _fwalk 函数返回结果。而 follow_syslinks 为False时，后面的(st.S_ISDIR(orig_st.st_mode) and path.samestat(orig_st, stat(topfd))) 可以生效，首先判断 top 的st_mode(文件的类型和存取的权限)是目录而不是符号链接，然后判断 top 和 top的文件描述符(topfd) 的 st_dev(文件的设备编号) 和 st_dev(节点) 是相同的，只有两个条件都成立时才继续往下调用 _fwalk 函数来返回结果。 _fwalk 函数使用文件描述符进行操作，避免直接操作 top 目录时，中途被伪造成同名的指向其他路径的符号链接。并且经过前面两个条件的验证，保证了传入是原来的 top 目录的文件描述符。</p>
<p>下面的 _fwalk 逻辑和前面的 walk 基本类似，不同的地方是这里递归时用的是文件描述符。</p>
<h3 id="第531-654行-exec函数族"><a href="#第531-654行-exec函数族" class="headerlink" title="第531~654行 exec函数族"></a>第531~654行 exec函数族</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">execl</span>(<span class="params">file, *args</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;execl(file, *args)</span></span><br><span class="line"><span class="string">    Execute the executable file with argument list args, replacing the</span></span><br><span class="line"><span class="string">    current process. &quot;&quot;&quot;</span></span><br><span class="line">    execv(file, args)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">execle</span>(<span class="params">file, *args</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;execle(file, *args, env)</span></span><br><span class="line"><span class="string">    Execute the executable file with argument list args and</span></span><br><span class="line"><span class="string">    environment env, replacing the current process. &quot;&quot;&quot;</span></span><br><span class="line">    env = args[-<span class="number">1</span>]</span><br><span class="line">    execve(file, args[:-<span class="number">1</span>], env)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">execlp</span>(<span class="params">file, *args</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;execlp(file, *args)</span></span><br><span class="line"><span class="string">    Execute the executable file (which is searched for along $PATH)</span></span><br><span class="line"><span class="string">    with argument list args, replacing the current process. &quot;&quot;&quot;</span></span><br><span class="line">    execvp(file, args)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">execlpe</span>(<span class="params">file, *args</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;execlpe(file, *args, env)</span></span><br><span class="line"><span class="string">    Execute the executable file (which is searched for along $PATH)</span></span><br><span class="line"><span class="string">    with argument list args and environment env, replacing the current</span></span><br><span class="line"><span class="string">    process. &quot;&quot;&quot;</span></span><br><span class="line">    env = args[-<span class="number">1</span>]</span><br><span class="line">    execvpe(file, args[:-<span class="number">1</span>], env)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">execvp</span>(<span class="params">file, args</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;execvp(file, args)</span></span><br><span class="line"><span class="string">    Execute the executable file (which is searched for along $PATH)</span></span><br><span class="line"><span class="string">    with argument list args, replacing the current process.</span></span><br><span class="line"><span class="string">    args may be a list or tuple of strings. &quot;&quot;&quot;</span></span><br><span class="line">    _execvpe(file, args)</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">execvpe</span>(<span class="params">file, args, env</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;execvpe(file, args, env)</span></span><br><span class="line"><span class="string">    Execute the executable file (which is searched for along $PATH)</span></span><br><span class="line"><span class="string">    with argument list args and environment env, replacing the</span></span><br><span class="line"><span class="string">    current process.</span></span><br><span class="line"><span class="string">    args may be a list or tuple of strings. &quot;&quot;&quot;</span></span><br><span class="line">    _execvpe(file, args, env)</span><br><span class="line"> </span><br><span class="line">__all__.extend([<span class="string">&quot;execl&quot;</span>,<span class="string">&quot;execle&quot;</span>,<span class="string">&quot;execlp&quot;</span>,<span class="string">&quot;execlpe&quot;</span>,<span class="string">&quot;execvp&quot;</span>,<span class="string">&quot;execvpe&quot;</span>])</span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_execvpe</span>(<span class="params">file, args, env=<span class="literal">None</span></span>):</span></span><br><span class="line">    <span class="keyword">if</span> env <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">        exec_func = execve</span><br><span class="line">        argrest = (args, env)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        exec_func = execv</span><br><span class="line">        argrest = (args,)</span><br><span class="line">        env = environ</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span> path.dirname(file):</span><br><span class="line">        exec_func(file, *argrest)</span><br><span class="line">        <span class="keyword">return</span></span><br><span class="line">    saved_exc = <span class="literal">None</span></span><br><span class="line">    path_list = get_exec_path(env)</span><br><span class="line">    <span class="keyword">if</span> name != <span class="string">&#x27;nt&#x27;</span>:</span><br><span class="line">        file = fsencode(file)</span><br><span class="line">        path_list = <span class="built_in">map</span>(fsencode, path_list)</span><br><span class="line">    <span class="keyword">for</span> <span class="built_in">dir</span> <span class="keyword">in</span> path_list:</span><br><span class="line">        fullname = path.join(<span class="built_in">dir</span>, file)</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            exec_func(fullname, *argrest)</span><br><span class="line">        <span class="keyword">except</span> (FileNotFoundError, NotADirectoryError) <span class="keyword">as</span> e:</span><br><span class="line">            last_exc = e</span><br><span class="line">        <span class="keyword">except</span> OSError <span class="keyword">as</span> e:</span><br><span class="line">            last_exc = e</span><br><span class="line">            <span class="keyword">if</span> saved_exc <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">                saved_exc = e</span><br><span class="line">    <span class="keyword">if</span> saved_exc <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">        <span class="keyword">raise</span> saved_exc</span><br><span class="line">    <span class="keyword">raise</span> last_exc</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_exec_path</span>(<span class="params">env=<span class="literal">None</span></span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Returns the sequence of directories that will be searched for the</span></span><br><span class="line"><span class="string">    named executable (similar to a shell) when launching a process.</span></span><br><span class="line"><span class="string">    *env* must be an environment variable dict or None.  If *env* is None,</span></span><br><span class="line"><span class="string">    os.environ will be used.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># Use a local import instead of a global import to limit the number of</span></span><br><span class="line">    <span class="comment"># modules loaded at startup: the os module is always loaded at startup by</span></span><br><span class="line">    <span class="comment"># Python. It may also avoid a bootstrap issue.</span></span><br><span class="line">    <span class="keyword">import</span> warnings</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span> env <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        env = environ</span><br><span class="line"> </span><br><span class="line">    <span class="comment"># &#123;b&#x27;PATH&#x27;: ...&#125;.get(&#x27;PATH&#x27;) and &#123;&#x27;PATH&#x27;: ...&#125;.get(b&#x27;PATH&#x27;) emit a</span></span><br><span class="line">    <span class="comment"># BytesWarning when using python -b or python -bb: ignore the warning</span></span><br><span class="line">    <span class="keyword">with</span> warnings.catch_warnings():</span><br><span class="line">        warnings.simplefilter(<span class="string">&quot;ignore&quot;</span>, BytesWarning)</span><br><span class="line"> </span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            path_list = env.get(<span class="string">&#x27;PATH&#x27;</span>)</span><br><span class="line">        <span class="keyword">except</span> TypeError:</span><br><span class="line">            path_list = <span class="literal">None</span></span><br><span class="line"> </span><br><span class="line">        <span class="keyword">if</span> supports_bytes_environ:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                path_listb = env[<span class="string">b&#x27;PATH&#x27;</span>]</span><br><span class="line">            <span class="keyword">except</span> (KeyError, TypeError):</span><br><span class="line">                <span class="keyword">pass</span></span><br><span class="line">            <span class="keyword">else</span>:</span><br><span class="line">                <span class="keyword">if</span> path_list <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">                    <span class="keyword">raise</span> ValueError(</span><br><span class="line">                        <span class="string">&quot;env cannot contain &#x27;PATH&#x27; and b&#x27;PATH&#x27; keys&quot;</span>)</span><br><span class="line">                path_list = path_listb</span><br><span class="line"> </span><br><span class="line">            <span class="keyword">if</span> path_list <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span> <span class="keyword">and</span> <span class="built_in">isinstance</span>(path_list, <span class="built_in">bytes</span>):</span><br><span class="line">                path_list = fsdecode(path_list)</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">if</span> path_list <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        path_list = defpath</span><br><span class="line">    <span class="keyword">return</span> path_list.split(pathsep)</span><br></pre></td></tr></table></figure>
<p>这里的6个函数类似Linux中6个以exec开头的函数族：execl、execv、execle、execve、execlp、execvp。(并非一一对应)</p>
<p>Linux中的 exec 函数族提供了一种方法，在不改变进程号的情况下用新的进程替代原来的进程。它可以根据指定的文件名或目录名找到可执行文件，并用它来取代原调用进程的数据段、代码段和堆栈段，在执行完之后，原调用进程的内容除了进程号外，其他全部被新程序的内容替换了。这6个函数中真正的系统调用只有execve，其他5个都是库函数，它们最终都会调用execve这个系统调用。(详见 <a target="_blank" rel="noopener" href="https://blog.csdn.net/guoping16/article/details/6583383">exec函数</a>)</p>
<p>第531~536行，<strong>execl</strong> 函数，对应Linux中的<strong>execl</strong>函数，作用是调用python内置函数execv执行一个可执行文件替代现有进程，入参是文件完整路径和参数。</p>
<blockquote>
<p>示例： <strong>os.execl(‘/usr/bin/cat’, ‘/root/test.py’, ‘/root/test2.py’)</strong></p>
</blockquote>
<p>第538~544行，<strong>execle</strong> 函数，对应Linux中的<strong>execle</strong>函数，作用是调用python内置函数execve执行一个可执行文件替代现有进程，入参是文件完整路径、参数、新程序的环境变量路径(最后一个参数,字典类型)。这里注意第一个参数要传入新程序的完整路径。传入的环境路径作为新程序的环境变量路径。</p>
<blockquote>
<p>示例 ：<strong>os.execle(‘/usr/bin/cat’, ‘/root/test.py’, ‘/root/test2.py’, {‘SYS’: ‘/root/bin’})</strong></p>
</blockquote>
<p>第546~551行，<strong>execlp</strong> 函数，对应Linux中的<strong>execlp</strong>函数，作用是调用python内置函数execvp执行一个可执行文件替代现有进程，入参是文件名、参数。这里第一个入参只需要填写新程序的文件名，会去系统默认环境变量路径查找该文件。</p>
<blockquote>
<p>示例 ：<strong>os.execlp(‘cat’, ‘/root/test.py’, ‘/root/test2.py’)</strong></p>
</blockquote>
<p>第553~560行，<strong>execlpe</strong> 函数，对应Linux中的<strong>execve</strong>函数，作用是调用python内置函数execvpe执行一个可执行文件替代现有进程，入参是文件名、参数、新程序的环境变量路径。这里注意第一个入参只需要填写新程序的文件名，会去传入的环境路径查找该文件，如果没有则去系统默认环境变量PATH查找。</p>
<blockquote>
<p>示例 ：<strong>os.execlpe(‘cat’, ‘/root/test.py’, ‘/root/test2.py’, {‘SYS’: ‘/root/bin’})</strong></p>
</blockquote>
<p><strong>注意</strong>下面这种情况，比如 cat 程序是在系统环境变量PATH中的’/usr/bin’目录下，传入的环境变量覆盖了系统默认环境变量PATH，没有加’/usr/bin’，此时将找不到 cat (示例1)。此时需要加上 cat 所在的目录，用冒号”<strong>:</strong>“隔开(示例2，示例3)。</p>
<blockquote>
<p>示例 1：<strong>os.execlpe(‘cat’, ‘/root/test.py’, ‘/root/test2.py’, {‘PATH’: ‘/root/bin’})</strong></p>
<p>示例 2：<strong>os.execlpe(‘cat’, ‘/root/test.py’, ‘/root/test2.py’, {‘PATH’: ‘/root/bin:/usr/bin**</strong>‘})**</p>
<p>示例 2：<strong>os.execlpe(‘cat’, ‘/root/test.py’, ‘/root/test2.py’, {‘SYS’: ‘/root/bin:/usr/bin’})</strong></p>
</blockquote>
<p> 第562<del>568行，<strong>execvp</strong> 函数，作用同第546</del>551行 <strong>execlp</strong> 函数，依然对应Linux中的 <strong>execlp</strong> 函数。不同的地方是这里的参数要用列表类型传入。</p>
<blockquote>
<p>示例 ：<strong>os.execvp(‘cat’, [‘/root/test.py’, ‘/root/test2.py’])</strong></p>
</blockquote>
<p> 第570<del>577行，<strong>execvpe</strong> 函数，作用同第553</del>560行 <strong>execlpe</strong> 函数，依然对应Linux中的 <strong>execve</strong> 函数。不同的地方是这里的参数要用列表类型传入。</p>
<blockquote>
<p>示例 ：<strong>os.execvpe(‘cat’, [‘/root/test.py’, ‘/root/test2.py’], {‘SYS’: ‘/root/bin’})</strong></p>
</blockquote>
<p>以上可以看出这6个<strong>exec</strong>函数和Linux原生的6个<strong>exec</strong>函数虽然功能类似，但是还是有些区别的，需要使用时要注意一些细节。</p>
<p>第581~ 654行是<strong>_execvpe</strong> 的定义，<strong>execvp</strong> 函数和<strong>execvpe</strong> 函数均调用此函数。<strong>_execvpe</strong> 将列表参数解成字符串参数，最终还是落到execv和execve函数上。这一段的几个函数看着很冗余，不够优雅，不知道大佬们写的时候怎么想的。</p>
<h3 id="第657-713行-Environ——环境变量信息的类"><a href="#第657-713行-Environ——环境变量信息的类" class="headerlink" title="第657~713行 _Environ——环境变量信息的类"></a>第657~713行 _Environ——环境变量信息的类</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Change environ to automatically call putenv(), unsetenv if they exist.</span></span><br><span class="line"><span class="keyword">from</span> _collections_abc <span class="keyword">import</span> MutableMapping</span><br><span class="line"> </span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_Environ</span>(<span class="params">MutableMapping</span>):</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, data, encodekey, decodekey, encodevalue, decodevalue, putenv, unsetenv</span>):</span></span><br><span class="line">        self.encodekey = encodekey</span><br><span class="line">        self.decodekey = decodekey</span><br><span class="line">        self.encodevalue = encodevalue</span><br><span class="line">        self.decodevalue = decodevalue</span><br><span class="line">        self.putenv = putenv</span><br><span class="line">        self.unsetenv = unsetenv</span><br><span class="line">        self._data = data</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span>(<span class="params">self, key</span>):</span></span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            value = self._data[self.encodekey(key)]</span><br><span class="line">        <span class="keyword">except</span> KeyError:</span><br><span class="line">            <span class="comment"># raise KeyError with the original key value</span></span><br><span class="line">            <span class="keyword">raise</span> KeyError(key) <span class="keyword">from</span> <span class="literal">None</span></span><br><span class="line">        <span class="keyword">return</span> self.decodevalue(value)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__setitem__</span>(<span class="params">self, key, value</span>):</span></span><br><span class="line">        key = self.encodekey(key)</span><br><span class="line">        value = self.encodevalue(value)</span><br><span class="line">        self.putenv(key, value)</span><br><span class="line">        self._data[key] = value</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__delitem__</span>(<span class="params">self, key</span>):</span></span><br><span class="line">        encodedkey = self.encodekey(key)</span><br><span class="line">        self.unsetenv(encodedkey)</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">del</span> self._data[encodedkey]</span><br><span class="line">        <span class="keyword">except</span> KeyError:</span><br><span class="line">            <span class="comment"># raise KeyError with the original key value</span></span><br><span class="line">            <span class="keyword">raise</span> KeyError(key) <span class="keyword">from</span> <span class="literal">None</span></span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="comment"># list() from dict object is an atomic operation</span></span><br><span class="line">        keys = <span class="built_in">list</span>(self._data)</span><br><span class="line">        <span class="keyword">for</span> key <span class="keyword">in</span> keys:</span><br><span class="line">            <span class="keyword">yield</span> self.decodekey(key)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__len__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">len</span>(self._data)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> <span class="string">&#x27;environ(&#123;&#123;&#123;&#125;&#125;&#125;)&#x27;</span>.<span class="built_in">format</span>(<span class="string">&#x27;, &#x27;</span>.join(</span><br><span class="line">            (<span class="string">&#x27;&#123;!r&#125;: &#123;!r&#125;&#x27;</span>.<span class="built_in">format</span>(self.decodekey(key), self.decodevalue(value))</span><br><span class="line">            <span class="keyword">for</span> key, value <span class="keyword">in</span> self._data.items())))</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">copy</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">dict</span>(self)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">setdefault</span>(<span class="params">self, key, value</span>):</span></span><br><span class="line">        <span class="keyword">if</span> key <span class="keyword">not</span> <span class="keyword">in</span> self:</span><br><span class="line">            self[key] = value</span><br><span class="line">        <span class="keyword">return</span> self[key]</span><br></pre></td></tr></table></figure>
<p>这个类实际上就是生成 os.environ 的类，能够获取系统所有的环境变量信息。</p>
<p>第658行，从抽象类模块_collections_abc 导入 MutableMapping类，python3中这个类也是字典的父类。平时我们经常用os.environ.get(‘xxx’)，和字典很类似，就是因为_Environ这个类重写了<strong>getitem</strong>()等方法。但是os.environ并不是字典类型，如果用 print(type(os.environ)) 会发现，它的类型是 &lt;class ‘os._Environ’&gt; 。</p>
<p>为什么要定义 encode和decode 那几个入参，见772~794行。</p>
<h3 id="第715-721行-os-putenv——设置环境变量"><a href="#第715-721行-os-putenv——设置环境变量" class="headerlink" title="第715~721行 os.putenv——设置环境变量"></a>第715~721行 os.putenv——设置环境变量</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    _putenv = putenv</span><br><span class="line"><span class="keyword">except</span> NameError:</span><br><span class="line">    _putenv = <span class="keyword">lambda</span> key, value: <span class="literal">None</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;putenv&quot;</span> <span class="keyword">not</span> <span class="keyword">in</span> __all__:</span><br><span class="line">        __all__.append(<span class="string">&quot;putenv&quot;</span>)</span><br></pre></td></tr></table></figure>
<p>调用 nt 或者 posix 库的 putenv 方法， 通过**os.putenv(‘key’, ‘value’)**将名为 key 的环境变量值设置为 value。该变量名修改会影响由 os.system(),popen(),fork()和execv()发起的子进程。但是putenv()的调用不会更新 os.environ，因此最好使用os.environ来更改环境变量。</p>
<h3 id="第723-729行-os-unsetenv——删除环境变量"><a href="#第723-729行-os-unsetenv——删除环境变量" class="headerlink" title="第723~729行 os.unsetenv——删除环境变量"></a>第723~729行 os.unsetenv——删除环境变量</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    _unsetenv = unsetenv</span><br><span class="line"><span class="keyword">except</span> NameError:</span><br><span class="line">    _unsetenv = <span class="keyword">lambda</span> key: _putenv(key, <span class="string">&quot;&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;unsetenv&quot;</span> <span class="keyword">not</span> <span class="keyword">in</span> __all__:</span><br><span class="line">        __all__.append(<span class="string">&quot;unsetenv&quot;</span>)</span><br></pre></td></tr></table></figure>
<h3 id="第723-729行-os-unsetenv——删除环境变量-1"><a href="#第723-729行-os-unsetenv——删除环境变量-1" class="headerlink" title="第723~729行 os.unsetenv——删除环境变量"></a>第723~729行 os.unsetenv——删除环境变量</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    _unsetenv = unsetenv</span><br><span class="line"><span class="keyword">except</span> NameError:</span><br><span class="line">    _unsetenv = <span class="keyword">lambda</span> key: _putenv(key, <span class="string">&quot;&quot;</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&quot;unsetenv&quot;</span> <span class="keyword">not</span> <span class="keyword">in</span> __all__:</span><br><span class="line">        __all__.append(<span class="string">&quot;unsetenv&quot;</span>)</span><br></pre></td></tr></table></figure>
<p>调用 nt 或者 posix 库的 unsetenv 方法， 通过**os.unsetenv (‘key’)**删除名为 key 的环境变量。该变量名删除会影响由 os.system(),popen(),fork()和execv()发起的子进程。但是unsetenv()的调用不会更新 os.environ，因此最好直接删除 os.environ 中的变量。</p>
<h3 id="第731-759行-os-environ——环境变量信息"><a href="#第731-759行-os-environ——环境变量信息" class="headerlink" title="第731~759行 os.environ——环境变量信息"></a>第731~759行 os.environ——环境变量信息</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_createenviron</span>():</span></span><br><span class="line">    <span class="keyword">if</span> name == <span class="string">&#x27;nt&#x27;</span>:</span><br><span class="line">        <span class="comment"># Where Env Var Names Must Be UPPERCASE</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">check_str</span>(<span class="params">value</span>):</span></span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, <span class="built_in">str</span>):</span><br><span class="line">                <span class="keyword">raise</span> TypeError(<span class="string">&quot;str expected, not %s&quot;</span> % <span class="built_in">type</span>(value).__name__)</span><br><span class="line">            <span class="keyword">return</span> value</span><br><span class="line">        encode = check_str</span><br><span class="line">        decode = <span class="built_in">str</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">encodekey</span>(<span class="params">key</span>):</span></span><br><span class="line">            <span class="keyword">return</span> encode(key).upper()</span><br><span class="line">        data = &#123;&#125;</span><br><span class="line">        <span class="keyword">for</span> key, value <span class="keyword">in</span> environ.items():</span><br><span class="line">            data[encodekey(key)] = value</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># Where Env Var Names Can Be Mixed Case</span></span><br><span class="line">        encoding = sys.getfilesystemencoding()</span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">encode</span>(<span class="params">value</span>):</span></span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, <span class="built_in">str</span>):</span><br><span class="line">                <span class="keyword">raise</span> TypeError(<span class="string">&quot;str expected, not %s&quot;</span> % <span class="built_in">type</span>(value).__name__)</span><br><span class="line">            <span class="keyword">return</span> value.encode(encoding, <span class="string">&#x27;surrogateescape&#x27;</span>)</span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">decode</span>(<span class="params">value</span>):</span></span><br><span class="line">            <span class="keyword">return</span> value.decode(encoding, <span class="string">&#x27;surrogateescape&#x27;</span>)</span><br><span class="line">        encodekey = encode</span><br><span class="line">        data = environ</span><br><span class="line">    <span class="keyword">return</span> _Environ(data,</span><br><span class="line">        encodekey, decode,</span><br><span class="line">        encode, decode,</span><br><span class="line">        _putenv, _unsetenv)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># unicode environ</span></span><br><span class="line">environ = _createenviron()</span><br><span class="line"><span class="keyword">del</span> _createenviron</span><br></pre></td></tr></table></figure>
<p>这段代码就是生成 os.environ 的过程。注意743行和755行，用到了 environ，而搜索整个 os.py 文件，只有在后面762行定义了 environ ，看上去好像自己定义自己了，这是怎么回事呢？</p>
<p>因为此 environ 非彼 environ，经过打桩调试，可以发现743行和755行的 environ 是字典类型，表示当前系统的所有环境变量的键值对，是在53行(from posix import *)或73行(from nt import *)从内置库引入的(取决于系统类型)。这段代码太有迷惑性了，还是挺坑的。理解了这个，也就可以明白 os.environ 其实还是对 nt 或者 posix 库里的environ的一层封装，整个os库其实做的就是封装这件事。至于为啥把原来的字典类型变成一种新的类型，我也不知道，额 ~。</p>
<h3 id="第766-770行-getenv——获取环境变量"><a href="#第766-770行-getenv——获取环境变量" class="headerlink" title="第766~770行 getenv——获取环境变量"></a>第766~770行 getenv——获取环境变量</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getenv</span>(<span class="params">key, default=<span class="literal">None</span></span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Get an environment variable, return None if it doesn&#x27;t exist.</span></span><br><span class="line"><span class="string">    The optional second argument can specify an alternate default.</span></span><br><span class="line"><span class="string">    key, default and the result are str.&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> environ.get(key, default)</span><br></pre></td></tr></table></figure>
<p> 通过**os.unsetenv (‘key’)**获取环境变量key的值，如果存在，返回环境变量key的值，否则返回default的值。key和返回值均为str字符串类型。</p>
<h3 id="第772-794行-environb，getenvb——字节型-byte-环境变量"><a href="#第772-794行-environb，getenvb——字节型-byte-环境变量" class="headerlink" title="第772~794行 environb，getenvb——字节型(byte)环境变量"></a>第772~794行 environb，getenvb——字节型(byte)环境变量</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">supports_bytes_environ = (name != <span class="string">&#x27;nt&#x27;</span>)</span><br><span class="line">__all__.extend((<span class="string">&quot;getenv&quot;</span>, <span class="string">&quot;supports_bytes_environ&quot;</span>))</span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> supports_bytes_environ:</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_check_bytes</span>(<span class="params">value</span>):</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(value, <span class="built_in">bytes</span>):</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">&quot;bytes expected, not %s&quot;</span> % <span class="built_in">type</span>(value).__name__)</span><br><span class="line">        <span class="keyword">return</span> value</span><br><span class="line"> </span><br><span class="line">    <span class="comment"># bytes environ</span></span><br><span class="line">    environb = _Environ(environ._data,</span><br><span class="line">        _check_bytes, <span class="built_in">bytes</span>,</span><br><span class="line">        _check_bytes, <span class="built_in">bytes</span>,</span><br><span class="line">        _putenv, _unsetenv)</span><br><span class="line">    <span class="keyword">del</span> _check_bytes</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">getenvb</span>(<span class="params">key, default=<span class="literal">None</span></span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Get an environment variable, return None if it doesn&#x27;t exist.</span></span><br><span class="line"><span class="string">        The optional second argument can specify an alternate default.</span></span><br><span class="line"><span class="string">        key, default and the result are bytes.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> environb.get(key, default)</span><br><span class="line"> </span><br><span class="line">    __all__.extend((<span class="string">&quot;environb&quot;</span>, <span class="string">&quot;getenvb&quot;</span>))</span><br></pre></td></tr></table></figure>
<p>设置标志位 supports_bytes_environ 判断当前系统类型是否是Windows。顾名思义，Windows不支持byte类型的环境变量。</p>
<p>在支持byte类型的环境变量的系统中，定义byte类型的 <strong>os.environb</strong> 对应字符串类型的 <strong>os.environ ,</strong> 定义byte类型的 <strong>os.getenvb</strong> 对应字符串类型的 <strong>os.getenv</strong> 。到这里也就明白为什么前面 _Environ 类要定义 encode和decode 那些入参了。</p>
<h3 id="第796-827行-fsencode，fsdecode——路径名的编码和解码"><a href="#第796-827行-fsencode，fsdecode——路径名的编码和解码" class="headerlink" title="第796~827行 fsencode，fsdecode——路径名的编码和解码"></a>第796~827行 fsencode，fsdecode——路径名的编码和解码</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_fscodec</span>():</span></span><br><span class="line">    encoding = sys.getfilesystemencoding()</span><br><span class="line">    errors = sys.getfilesystemencodeerrors()</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">fsencode</span>(<span class="params">filename</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Encode filename (an os.PathLike, bytes, or str) to the filesystem</span></span><br><span class="line"><span class="string">        encoding with &#x27;surrogateescape&#x27; error handler, return bytes unchanged.</span></span><br><span class="line"><span class="string">        On Windows, use &#x27;strict&#x27; error handler if the file system encoding is</span></span><br><span class="line"><span class="string">        &#x27;mbcs&#x27; (which is the default encoding).</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        filename = fspath(filename)  <span class="comment"># Does type-checking of `filename`.</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(filename, <span class="built_in">str</span>):</span><br><span class="line">            <span class="keyword">return</span> filename.encode(encoding, errors)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> filename</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">fsdecode</span>(<span class="params">filename</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Decode filename (an os.PathLike, bytes, or str) from the filesystem</span></span><br><span class="line"><span class="string">        encoding with &#x27;surrogateescape&#x27; error handler, return str unchanged. On</span></span><br><span class="line"><span class="string">        Windows, use &#x27;strict&#x27; error handler if the file system encoding is</span></span><br><span class="line"><span class="string">        &#x27;mbcs&#x27; (which is the default encoding).</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        filename = fspath(filename)  <span class="comment"># Does type-checking of `filename`.</span></span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">isinstance</span>(filename, <span class="built_in">bytes</span>):</span><br><span class="line">            <span class="keyword">return</span> filename.decode(encoding, errors)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> filename</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">return</span> fsencode, fsdecode</span><br><span class="line"> </span><br><span class="line">fsencode, fsdecode = _fscodec()</span><br><span class="line"><span class="keyword">del</span> _fscodec</span><br></pre></td></tr></table></figure>
<p>先将路径名转化成标准路径名，即 os.PathLike 的，然后再进行编码和解码的操作，没啥好说的。</p>
<h3 id="第829-970行-spawn函数族"><a href="#第829-970行-spawn函数族" class="headerlink" title="第829~970行 spawn函数族"></a>第829~970行 spawn函数族</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Supply spawn*() (probably only for Unix)</span></span><br><span class="line"><span class="keyword">if</span> _exists(<span class="string">&quot;fork&quot;</span>) <span class="keyword">and</span> <span class="keyword">not</span> _exists(<span class="string">&quot;spawnv&quot;</span>) <span class="keyword">and</span> _exists(<span class="string">&quot;execv&quot;</span>):</span><br><span class="line"> </span><br><span class="line">    P_WAIT = <span class="number">0</span></span><br><span class="line">    P_NOWAIT = P_NOWAITO = <span class="number">1</span></span><br><span class="line"> </span><br><span class="line">    __all__.extend([<span class="string">&quot;P_WAIT&quot;</span>, <span class="string">&quot;P_NOWAIT&quot;</span>, <span class="string">&quot;P_NOWAITO&quot;</span>])</span><br><span class="line"> </span><br><span class="line">    <span class="comment"># XXX Should we support P_DETACH?  I suppose it could fork()**2</span></span><br><span class="line">    <span class="comment"># and close the std I/O streams.  Also, P_OVERLAY is the same</span></span><br><span class="line">    <span class="comment"># as execv*()?</span></span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">_spawnvef</span>(<span class="params">mode, file, args, env, func</span>):</span></span><br><span class="line">        <span class="comment"># Internal helper; func is the exec*() function to use</span></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(args, (<span class="built_in">tuple</span>, <span class="built_in">list</span>)):</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">&#x27;argv must be a tuple or a list&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> args <span class="keyword">or</span> <span class="keyword">not</span> args[<span class="number">0</span>]:</span><br><span class="line">            <span class="keyword">raise</span> ValueError(<span class="string">&#x27;argv first element cannot be empty&#x27;</span>)</span><br><span class="line">        pid = fork()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> pid:</span><br><span class="line">            <span class="comment"># Child</span></span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                <span class="keyword">if</span> env <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">                    func(file, args)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    func(file, args, env)</span><br><span class="line">            <span class="keyword">except</span>:</span><br><span class="line">                _exit(<span class="number">127</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="comment"># Parent</span></span><br><span class="line">            <span class="keyword">if</span> mode == P_NOWAIT:</span><br><span class="line">                <span class="keyword">return</span> pid <span class="comment"># Caller is responsible for waiting!</span></span><br><span class="line">            <span class="keyword">while</span> <span class="number">1</span>:</span><br><span class="line">                wpid, sts = waitpid(pid, <span class="number">0</span>)</span><br><span class="line">                <span class="keyword">if</span> WIFSTOPPED(sts):</span><br><span class="line">                    <span class="keyword">continue</span></span><br><span class="line">                <span class="keyword">elif</span> WIFSIGNALED(sts):</span><br><span class="line">                    <span class="keyword">return</span> -WTERMSIG(sts)</span><br><span class="line">                <span class="keyword">elif</span> WIFEXITED(sts):</span><br><span class="line">                    <span class="keyword">return</span> WEXITSTATUS(sts)</span><br><span class="line">                <span class="keyword">else</span>:</span><br><span class="line">                    <span class="keyword">raise</span> OSError(<span class="string">&quot;Not stopped, signaled or exited???&quot;</span>)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnv</span>(<span class="params">mode, file, args</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnv(mode, file, args) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file with arguments from args in a subprocess.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> _spawnvef(mode, file, args, <span class="literal">None</span>, execv)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnve</span>(<span class="params">mode, file, args, env</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnve(mode, file, args, env) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file with arguments from args in a subprocess with the</span></span><br><span class="line"><span class="string">specified environment.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> _spawnvef(mode, file, args, env, execve)</span><br><span class="line"> </span><br><span class="line">    <span class="comment"># Note: spawnvp[e] isn&#x27;t currently supported on Windows</span></span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnvp</span>(<span class="params">mode, file, args</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnvp(mode, file, args) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file (which is looked for along $PATH) with arguments from</span></span><br><span class="line"><span class="string">args in a subprocess.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> _spawnvef(mode, file, args, <span class="literal">None</span>, execvp)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnvpe</span>(<span class="params">mode, file, args, env</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnvpe(mode, file, args, env) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file (which is looked for along $PATH) with arguments from</span></span><br><span class="line"><span class="string">args in a subprocess with the supplied environment.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> _spawnvef(mode, file, args, env, execvpe)</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line">    __all__.extend([<span class="string">&quot;spawnv&quot;</span>, <span class="string">&quot;spawnve&quot;</span>, <span class="string">&quot;spawnvp&quot;</span>, <span class="string">&quot;spawnvpe&quot;</span>])</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> _exists(<span class="string">&quot;spawnv&quot;</span>):</span><br><span class="line">    <span class="comment"># These aren&#x27;t supplied by the basic Windows code</span></span><br><span class="line">    <span class="comment"># but can be easily implemented in Python</span></span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnl</span>(<span class="params">mode, file, *args</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnl(mode, file, *args) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file with arguments from args in a subprocess.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> spawnv(mode, file, args)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnle</span>(<span class="params">mode, file, *args</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnle(mode, file, *args, env) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file with arguments from args in a subprocess with the</span></span><br><span class="line"><span class="string">supplied environment.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        env = args[-<span class="number">1</span>]</span><br><span class="line">        <span class="keyword">return</span> spawnve(mode, file, args[:-<span class="number">1</span>], env)</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line">    __all__.extend([<span class="string">&quot;spawnl&quot;</span>, <span class="string">&quot;spawnle&quot;</span>])</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> _exists(<span class="string">&quot;spawnvp&quot;</span>):</span><br><span class="line">    <span class="comment"># At the moment, Windows doesn&#x27;t implement spawnvp[e],</span></span><br><span class="line">    <span class="comment"># so it won&#x27;t have spawnlp[e] either.</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnlp</span>(<span class="params">mode, file, *args</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnlp(mode, file, *args) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file (which is looked for along $PATH) with arguments from</span></span><br><span class="line"><span class="string">args in a subprocess with the supplied environment.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">return</span> spawnvp(mode, file, args)</span><br><span class="line"> </span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">spawnlpe</span>(<span class="params">mode, file, *args</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;spawnlpe(mode, file, *args, env) -&gt; integer</span></span><br><span class="line"><span class="string">Execute file (which is looked for along $PATH) with arguments from</span></span><br><span class="line"><span class="string">args in a subprocess with the supplied environment.</span></span><br><span class="line"><span class="string">If mode == P_NOWAIT return the pid of the process.</span></span><br><span class="line"><span class="string">If mode == P_WAIT return the process&#x27;s exit code if it exits normally;</span></span><br><span class="line"><span class="string">otherwise return -SIG, where SIG is the signal that killed it. &quot;&quot;&quot;</span></span><br><span class="line">        env = args[-<span class="number">1</span>]</span><br><span class="line">        <span class="keyword">return</span> spawnvpe(mode, file, args[:-<span class="number">1</span>], env)</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line">    __all__.extend([<span class="string">&quot;spawnlp&quot;</span>, <span class="string">&quot;spawnlpe&quot;</span>])</span><br></pre></td></tr></table></figure>
<p>此处的 spawn 函数族对应 LInux 中的 spawn 函数族，并不完全相同。实际上，Linux中spawn()函数族与exec()函数族有相似也有区别。exec()是启动一个程序并取代原来的程序，spawn()函数既可以在结束原来的程序后启动另一个程序，也可以启动另一个程序并在该程序结束后返回到原来的程序，通过 mode 参数来控制。见 <a target="_blank" rel="noopener" href="https://blog.csdn.net/xiaosi2468/article/details/6401874">spawn 系列函数</a>。</p>
<p>第832~835行，定义 mode 参数的两个枚举值，P_WAIT = 0 ，表示阻塞的方式启动子进程，父进程挂起直到子进程执行完毕，P_NOWAIT = 1表示非阻塞的方式启动子进程，父进程和子进程同时运行。(Linux中mode还有另外一个枚举值 P_OVERLAY = 2 ，表示子进程将覆盖父进程原有的存储区位置。这与 exec族函数族调用是相同的，这也是为什么os库的spawn 函数族没有这个枚举值，下面的838行注释有说）。可以看出来spawn函数族和fork有点像，其实往下看代码可以知道，os库里的spawn确实是通过调用folk来实现的。</p>
<p>第841行，定义_spawnvef 函数，后面四个函数都是调用这个函数。</p>
<p>第843~846行，args 参数必须是元组或者列表，且不能为空。</p>
<p>第847行，folk一个子进程，获取返回pid。使用fork函数得到的子进程从父进程的继承了当前进程的整个地址空间，但是pid是不同的，所以可以根据这个来分别控制父进程和子进程的行为。关于fork见 <a target="_blank" rel="noopener" href="https://blog.csdn.net/jason314/article/details/5640969">linux中fork函数详解</a> 和 <a target="_blank" rel="noopener" href="https://www.unix.com/unix-for-advanced-and-expert-users/178644-spawn-vs-fork.html">spawn() Vs. fork()</a></p>
<p>第848~856行，在子进程中，fork返回0，所以进入 if not pid 分支，执行传入的func函数。出现异常时返回127，一般是环境变量或者当前路径没找到file这个文件。</p>
<p>第857~870行，在父进程中，fork返回新创建子进程的进程ID，所以进入 else 分支。如果是非阻塞模式即mode == P_NOWAIT，函数直接返回。如说是阻塞模式，则父进程一直检测子进程的状态，直到子进程执行结束。</p>
<p>第872~914行：</p>
<ul>
<li><strong>spawnv</strong> 函数， 调用_spawnvef，最终执行的是 execv，执行一个可执行文件替代现有进程。</li>
<li><strong>spawnve</strong> 函数， 调用_spawnvef，最终执行的是 execve，执行一个可执行文件替代现有进程。</li>
<li><strong>spawnvp</strong> 函数， 调用_spawnvef，最终执行的是 execvp，执行一个可执行文件替代现有进程。</li>
<li><strong>spawnvp</strong> 函数， 调用_spawnvef，最终执行的是 execvpe，执行一个可执行文件替代现有进程。</li>
</ul>
<p>第917~970行，这4个函数对应上面4个函数，不同的地方是将最后一个参数从列表或元组变为可变参数类型。例如调用<strong>spawnv(mode, file, [args1, args2, args3])</strong> 变成 <strong>spawnl(mode, file, args1, args2, args3)。</strong></p>
<ul>
<li><strong>spawnl</strong> 函数， 对应前面 <strong>spawnv</strong> 函数。</li>
<li><strong>spawnle</strong> 函数， 对应前面 <strong>spawnve</strong> 函数。</li>
<li><strong>spawnlp</strong> 函数， 对应前面 <strong>spawnvp</strong> 函数。</li>
<li><strong>spawnlpe</strong> 函数， 对应前面 <strong>spawnvp</strong> 函数。</li>
</ul>
<h3 id="第973-1016行-popen——执行cmd或者shell命令"><a href="#第973-1016行-popen——执行cmd或者shell命令" class="headerlink" title="第973~1016行 popen——执行cmd或者shell命令"></a>第973~1016行 popen——执行cmd或者shell命令</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Supply os.popen()</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">popen</span>(<span class="params">cmd, mode=<span class="string">&quot;r&quot;</span>, buffering=-<span class="number">1</span></span>):</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(cmd, <span class="built_in">str</span>):</span><br><span class="line">        <span class="keyword">raise</span> TypeError(<span class="string">&quot;invalid cmd type (%s, expected string)&quot;</span> % <span class="built_in">type</span>(cmd))</span><br><span class="line">    <span class="keyword">if</span> mode <span class="keyword">not</span> <span class="keyword">in</span> (<span class="string">&quot;r&quot;</span>, <span class="string">&quot;w&quot;</span>):</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;invalid mode %r&quot;</span> % mode)</span><br><span class="line">    <span class="keyword">if</span> buffering == <span class="number">0</span> <span class="keyword">or</span> buffering <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;popen() does not support unbuffered streams&quot;</span>)</span><br><span class="line">    <span class="keyword">import</span> subprocess, io</span><br><span class="line">    <span class="keyword">if</span> mode == <span class="string">&quot;r&quot;</span>:</span><br><span class="line">        proc = subprocess.Popen(cmd,</span><br><span class="line">                                shell=<span class="literal">True</span>,</span><br><span class="line">                                stdout=subprocess.PIPE,</span><br><span class="line">                                bufsize=buffering)</span><br><span class="line">        <span class="keyword">return</span> _wrap_close(io.TextIOWrapper(proc.stdout), proc)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        proc = subprocess.Popen(cmd,</span><br><span class="line">                                shell=<span class="literal">True</span>,</span><br><span class="line">                                stdin=subprocess.PIPE,</span><br><span class="line">                                bufsize=buffering)</span><br><span class="line">        <span class="keyword">return</span> _wrap_close(io.TextIOWrapper(proc.stdin), proc)</span><br><span class="line"> </span><br><span class="line"><span class="comment"># Helper for popen() -- a proxy for a file whose close waits for the process</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">_wrap_close</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, stream, proc</span>):</span></span><br><span class="line">        self._stream = stream</span><br><span class="line">        self._proc = proc</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">close</span>(<span class="params">self</span>):</span></span><br><span class="line">        self._stream.close()</span><br><span class="line">        returncode = self._proc.wait()</span><br><span class="line">        <span class="keyword">if</span> returncode == <span class="number">0</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line">        <span class="keyword">if</span> name == <span class="string">&#x27;nt&#x27;</span>:</span><br><span class="line">            <span class="keyword">return</span> returncode</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">return</span> returncode &lt;&lt; <span class="number">8</span>  <span class="comment"># Shift left to match old behavior</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__enter__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> self</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__exit__</span>(<span class="params">self, *args</span>):</span></span><br><span class="line">        self.close()</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__getattr__</span>(<span class="params">self, name</span>):</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">getattr</span>(self._stream, name)</span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__iter__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">iter</span>(self._stream)</span><br></pre></td></tr></table></figure>
<p>这个方法很常用，功能是执行一条命令，就像在cmd或者shell里执行一条命令一样。原来内部是使用 subprocess.Popen 实现的。下面重点看一下 io.TextIOWrapper 的意思：</p>
<p>为了使复杂的I/O软件具有清晰的结构，良好的可移植性和适应性，在I/O软件中普遍釆用了层次式结构，将系统输入/输出功能组织成一系列的层次，每一层都利用其下层提供的服务，完成输入/输出功能中的某些子功能，并屏蔽这些功能实现的细节，向高层提供服务。Python中主要分了三层，文本I/O，二进制I/O和原始I/O。每一种都有对应的类实现。I/O层次结构的顶部是抽象基类IOBase,它定义了流的基本接口，但读取和写入流之间没有分离。TextIOBase类继承IOBase,用于处理字节表示文本流，并从字符串处理编码和解码。而TextIOWrapper是TextIOBase类的子类，用来缓冲原始流BufferedIOBase的缓冲文本接口。(详见 <a target="_blank" rel="noopener" href="https://www.cnblogs.com/lincappu/p/12801539.html">python3之模块io</a> 和 <a target="_blank" rel="noopener" href="http://c.biancheng.net/cpp/html/2632.html">I/O子系统的层次结构</a>) 。 其实在这里只需要知道 os.Popen 和 原始subprocess.Popen 的区别是 os.Popen会把返回值用 _wrap_close 封装一次。</p>
<h3 id="第1018-1023行-fdopen——打开文件"><a href="#第1018-1023行-fdopen——打开文件" class="headerlink" title="第1018~1023行 fdopen——打开文件"></a>第1018~1023行 fdopen——打开文件</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Supply os.fdopen()</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fdopen</span>(<span class="params">fd, *args, **kwargs</span>):</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> <span class="built_in">isinstance</span>(fd, <span class="built_in">int</span>):</span><br><span class="line">        <span class="keyword">raise</span> TypeError(<span class="string">&quot;invalid fd type (%s, expected integer)&quot;</span> % <span class="built_in">type</span>(fd))</span><br><span class="line">    <span class="keyword">import</span> io</span><br><span class="line">    <span class="keyword">return</span> io.<span class="built_in">open</span>(fd, *args, **kwargs)</span><br></pre></td></tr></table></figure>
<p>传入打开文件描述符 <em>fd</em> ，返回对应文件的对象。类似内建 open 函数，二者接受同样的参数。不同之处在于fdopen第一个参数应该为整数(文件描述符)。关于最后的 r<strong>eturn  io.open</strong> , <a target="_blank" rel="noopener" href="https://docs.python.org/3.8/library/io.html#io.open">官方文档</a> 说 io.open 和内建函数 open 的区别是：io.open 是 open 的别名 (This is an alias for the builtin open() function)。</p>
<h3 id="第1026-1061行-fspath——路径标准化"><a href="#第1026-1061行-fspath——路径标准化" class="headerlink" title="第1026~1061行 fspath——路径标准化"></a>第1026~1061行 fspath——路径标准化</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># For testing purposes, make sure the function is available when the C</span></span><br><span class="line"><span class="comment"># implementation exists.</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_fspath</span>(<span class="params">path</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Return the path representation of a path-like object.</span></span><br><span class="line"><span class="string">    If str or bytes is passed in, it is returned unchanged. Otherwise the</span></span><br><span class="line"><span class="string">    os.PathLike interface is used to get the path representation. If the</span></span><br><span class="line"><span class="string">    path representation is not str or bytes, TypeError is raised. If the</span></span><br><span class="line"><span class="string">    provided path is not str, bytes, or os.PathLike, TypeError is raised.</span></span><br><span class="line"><span class="string">    &quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">isinstance</span>(path, (<span class="built_in">str</span>, <span class="built_in">bytes</span>)):</span><br><span class="line">        <span class="keyword">return</span> path</span><br><span class="line"> </span><br><span class="line">    <span class="comment"># Work from the object&#x27;s type to match method resolution of other magic</span></span><br><span class="line">    <span class="comment"># methods.</span></span><br><span class="line">    path_type = <span class="built_in">type</span>(path)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        path_repr = path_type.__fspath__(path)</span><br><span class="line">    <span class="keyword">except</span> AttributeError:</span><br><span class="line">        <span class="keyword">if</span> <span class="built_in">hasattr</span>(path_type, <span class="string">&#x27;__fspath__&#x27;</span>):</span><br><span class="line">            <span class="keyword">raise</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="keyword">raise</span> TypeError(<span class="string">&quot;expected str, bytes or os.PathLike object, &quot;</span></span><br><span class="line">                            <span class="string">&quot;not &quot;</span> + path_type.__name__)</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">isinstance</span>(path_repr, (<span class="built_in">str</span>, <span class="built_in">bytes</span>)):</span><br><span class="line">        <span class="keyword">return</span> path_repr</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">raise</span> TypeError(<span class="string">&quot;expected &#123;&#125;.__fspath__() to return str or bytes, &quot;</span></span><br><span class="line">                        <span class="string">&quot;not &#123;&#125;&quot;</span>.<span class="built_in">format</span>(path_type.__name__,</span><br><span class="line">                                        <span class="built_in">type</span>(path_repr).__name__))</span><br><span class="line"> </span><br><span class="line"><span class="comment"># If there is no C implementation, make the pure Python version the</span></span><br><span class="line"><span class="comment"># implementation as transparently as possible.</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> _exists(<span class="string">&#x27;fspath&#x27;</span>):</span><br><span class="line">    fspath = _fspath</span><br><span class="line">    fspath.__name__ = <span class="string">&quot;fspath&quot;</span></span><br></pre></td></tr></table></figure>
<p>第1028~1055行， _fspath 方法的作用是返回一个 os 模块能处理的标准路径形式，其实就是字符串。如果传入的是 str 或 bytes 类型的字符串，将原样返回。否则将调用传入参数的<code>__fspath__</code>方法，如果得到的是一个 str 或 bytes 类型的对象，那就返回这个值。<code>__fspath__</code> 方法应该是这样定义的：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__fspath__</span>(<span class="params">self</span>):</span></span><br><span class="line">    <span class="keyword">return</span> self.path.encode(<span class="string">&#x27;utf8&#x27;</span>)</span><br></pre></td></tr></table></figure>
<p> 第1059~1061行，只有在 buildin 的内建方法 globals() 里不包含 fspath 时，才会调用上面的_fspath方法去定义 os.fspath。实测 windows 下不会走这个分支，也就是说 Windows 下内建方法已经存在 fspath 了。</p>
<h3 id="第1064-1077行-PathLike——什么是PathLike"><a href="#第1064-1077行-PathLike——什么是PathLike" class="headerlink" title="第1064~1077行 PathLike——什么是PathLike"></a>第1064~1077行 PathLike——什么是PathLike</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PathLike</span>(<span class="params">abc.ABC</span>):</span></span><br><span class="line"></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Abstract base class for implementing the file system path protocol.&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">    @abc.abstractmethod</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__fspath__</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Return the file system path representation of the object.&quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">raise</span> NotImplementedError</span><br><span class="line"></span><br><span class="line"><span class="meta">    @classmethod</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__subclasshook__</span>(<span class="params">cls, subclass</span>):</span></span><br><span class="line">        <span class="keyword">if</span> cls <span class="keyword">is</span> PathLike:</span><br><span class="line">            <span class="keyword">return</span> _check_methods(subclass, <span class="string">&#x27;__fspath__&#x27;</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">NotImplemented</span></span><br></pre></td></tr></table></figure>
<p>前面提到好多次，路径要是 PathLike 的，这个抽象类就是为了解释到底什么样的类才是 “ PathLike的”。里面用到了@abc.abstractmethod 装饰器，说明这个类是抽象类不能被实例化。但是可以被继承，<code>__subclasshook__</code>检查子类是否实现了<code>__fspath__</code> 方法。也就是说一个类如果是 “PathLike 的”，首先它必须要实现 <code>__fspath__</code> 方法。</p>
<h3 id="第1080-1115行-add-dll-directory——-添加DLL文件搜索路径"><a href="#第1080-1115行-add-dll-directory——-添加DLL文件搜索路径" class="headerlink" title="第1080~1115行 add_dll_directory—— 添加DLL文件搜索路径"></a>第1080~1115行 add_dll_directory—— 添加DLL文件搜索路径</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> name == <span class="string">&#x27;nt&#x27;</span>:</span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">_AddedDllDirectory</span>:</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, path, cookie, remove_dll_directory</span>):</span></span><br><span class="line">            self.path = path</span><br><span class="line">            self._cookie = cookie</span><br><span class="line">            self._remove_dll_directory = remove_dll_directory</span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">close</span>(<span class="params">self</span>):</span></span><br><span class="line">            self._remove_dll_directory(self._cookie)</span><br><span class="line">            self.path = <span class="literal">None</span></span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">__enter__</span>(<span class="params">self</span>):</span></span><br><span class="line">            <span class="keyword">return</span> self</span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">__exit__</span>(<span class="params">self, *args</span>):</span></span><br><span class="line">            self.close()</span><br><span class="line">        <span class="function"><span class="keyword">def</span> <span class="title">__repr__</span>(<span class="params">self</span>):</span></span><br><span class="line">            <span class="keyword">if</span> self.path:</span><br><span class="line">                <span class="keyword">return</span> <span class="string">&quot;&amp;lt;AddedDllDirectory(&#123;!r&#125;)&amp;gt;&quot;</span>.<span class="built_in">format</span>(self.path)</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;&amp;lt;AddedDllDirectory()&amp;gt;&quot;</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">add_dll_directory</span>(<span class="params">path</span>):</span></span><br><span class="line">        <span class="string">&quot;&quot;&quot;Add a path to the DLL search path.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">        This search path is used when resolving dependencies for imported</span></span><br><span class="line"><span class="string">        extension modules (the module itself is resolved through sys.path),</span></span><br><span class="line"><span class="string">        and also by ctypes.</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">        Remove the directory by calling close() on the returned object or</span></span><br><span class="line"><span class="string">        using it in a with statement.</span></span><br><span class="line"><span class="string">        &quot;&quot;&quot;</span></span><br><span class="line">        <span class="keyword">import</span> nt</span><br><span class="line">        cookie = nt._add_dll_directory(path)</span><br><span class="line">        <span class="keyword">return</span> _AddedDllDirectory(</span><br><span class="line">            path,</span><br><span class="line">            cookie,</span><br><span class="line">            nt._remove_dll_directory</span><br><span class="line">        )</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>在Python中调用C/C++编写的dll文件时，需要提供这个dll文件的搜索路径(类似环境变量)。add_dll_directory 函数就用于在导入扩展模块或使用 ctypes 加载 DLL 时，依赖提供额外搜索路径。调用返回的_AddedDllDirectory 的close方法可以删除这个路径。主要调用 nt._add_dll_directory方法实现，所以只在Windows系统有效。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>《彻底弄懂Python标准库源码》计划，才完成第一篇，最大的感受就是读标准库源码跟我想象很不一样，太太太难了。我以为标准库一定都很通俗易懂吧，应该挺简单的吧，可事实上看过来，这些标准库方法在外面调用起来的确挺优雅的，它们的实现过程还挺复杂的。我们调用的时候只需要关注入参和出参是什么就行了，有时候感觉好神奇好方便啊，我传个A竟然能返回我想要的B。而研究源码就是要看它为什么是这样的，为什么这么神奇，这就要求每一行代码的意思都要搞明白。作为一个程序员，我们要一直保持着一颗好奇心，我们不会相信有什么黑科技，我们一定要看看这些 magic 的背后是什么原理 。</p>
<p>可能也和第一篇选的os库有关系，这个和系统交互的库，大部分是把C的库进行了一层封装。后半部分涉及很多系统底层的的东西，好多不了解的知识，导致看的很煎熬，中间各种查资料补课，还另外补充了几篇博客。好在没想过放弃，自己立的 flag 一定要把它完成。</p>
<p>本来想学习标准库的优雅写法，可是看完我发现我并学不会，os库也不是想象的那么优雅，更多的是为了更有效地实现功能。感觉要想达到学习代码写法的目的，还是看 requests 这种有名的第三方库比较好。还有这篇博客这种一行一行贴代码流水账式的分析意义也不大，白白占用很大篇幅，以后考虑换一种风格。</p>
<ol start="2">
<li><h3 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h3><ol>
<li>如果只是想看 os 库的用法，官方文档写的很全很详细</li>
</ol>
<p>​       官方文档 <a target="_blank" rel="noopener" href="https://docs.python.org/zh-cn/3/library/os.html">os — 多种操作系统接口</a> </p>
<p>​       官方文档 <a target="_blank" rel="noopener" href="https://docs.python.org/zh-cn/3/library/io.html">io — 处理流的核心工具</a></p>
<ol start="2">
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/107883964">彻底弄懂 Linux 下的文件描述符 —— 杰克小麻雀</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/107567001">彻底弄懂 Python3中入参里的*号的作用 —— 杰克小麻雀</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Symlink_race">Symlink race —- Wikipedia</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/u011150719/article/details/50865280">linux stat函数（获取文件详细信息）—— mastodon</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/guoping16/article/details/6583383">exec函数 —— guoping16</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/xiaosi2468/article/details/6401874">spawn 系列函数 —— xiaosi2468</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.csdn.net/jason314/article/details/5640969">linux中fork函数详解 —— jason314</a> </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.unix.com/unix-for-advanced-and-expert-users/178644-spawn-vs-fork.html">spawn() Vs. fork() —— The UNIX and Linux Forums</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/lincappu/p/12801539.html">python3之模块io —— lincappu</a> </p>
</li>
<li><p><a target="_blank" rel="noopener" href="http://c.biancheng.net/cpp/html/2632.html">I/O子系统的层次结构 —— C语言中文网</a><a target="_blank" rel="noopener" href="http://c.biancheng.net/cpp/html/2632.html">http://c.biancheng.net/cpp/html/2632.html</a>)</p>
</li>
</ol>
</li>
</ol>
<hr>
<p>前文见 </p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/106329590">彻底弄懂Python标准库源码（零）—— 学习计划</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/yushuaigee/article/details/106755148">彻底弄懂Python标准库源码（一）—— os模块</a></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <div class="declare">
      <ul class="post-copyright">
        <li>
          <i class="ri-copyright-line"></i>
          <strong>版权声明： </strong>
          
          本博客所有文章除特别声明外，著作权归作者所有。转载请注明出处！
          
        </li>
      </ul>
    </div>
    
    <footer class="article-footer">
       
<div class="share-btn">
      <span class="share-sns share-outer">
        <i class="ri-share-forward-line"></i>
        分享
      </span>
      <div class="share-wrap">
        <i class="arrow"></i>
        <div class="share-icons">
          
          <a class="weibo share-sns" href="javascript:;" data-type="weibo">
            <i class="ri-weibo-fill"></i>
          </a>
          <a class="weixin share-sns wxFab" href="javascript:;" data-type="weixin">
            <i class="ri-wechat-fill"></i>
          </a>
          <a class="qq share-sns" href="javascript:;" data-type="qq">
            <i class="ri-qq-fill"></i>
          </a>
          <a class="douban share-sns" href="javascript:;" data-type="douban">
            <i class="ri-douban-line"></i>
          </a>
          <!-- <a class="qzone share-sns" href="javascript:;" data-type="qzone">
            <i class="icon icon-qzone"></i>
          </a> -->
          
          <a class="facebook share-sns" href="javascript:;" data-type="facebook">
            <i class="ri-facebook-circle-fill"></i>
          </a>
          <a class="twitter share-sns" href="javascript:;" data-type="twitter">
            <i class="ri-twitter-fill"></i>
          </a>
          <a class="google share-sns" href="javascript:;" data-type="google">
            <i class="ri-google-fill"></i>
          </a>
        </div>
      </div>
</div>

<div class="wx-share-modal">
    <a class="modal-close" href="javascript:;"><i class="ri-close-circle-line"></i></a>
    <p>扫一扫，分享到微信</p>
    <div class="wx-qrcode">
      <img src="//api.qrserver.com/v1/create-qr-code/?size=150x150&data=http://example.com/2020/09/09/%E5%BD%BB%E5%BA%95%E5%BC%84%E6%87%82Python%E6%A0%87%E5%87%86%E5%BA%93%E6%BA%90%E7%A0%81%EF%BC%88%E4%BA%8C%EF%BC%89%E2%80%94%E2%80%94%20os%E6%A8%A1%E5%9D%97%EF%BC%88%E7%BB%AD%EF%BC%89/" alt="微信分享二维码">
    </div>
</div>

<div id="share-mask"></div>  
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/linux/" rel="tag">linux</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/os/" rel="tag">os</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/python/" rel="tag">python</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E7%BC%96%E7%A8%8B%E8%AF%AD%E8%A8%80/" rel="tag">编程语言</a></li></ul>

    </footer>
  </div>

   
  <nav class="article-nav">
    
      <a href="/2020/12/11/%E5%BD%BB%E5%BA%95%E5%BC%84%E6%87%82Python%E6%A0%87%E5%87%86%E5%BA%93%E6%BA%90%E7%A0%81%EF%BC%88%E4%B8%89%EF%BC%89%E2%80%94%E2%80%94%20pprint%E6%A8%A1%E5%9D%97/" class="article-nav-link">
        <strong class="article-nav-caption">上一篇</strong>
        <div class="article-nav-title">
          
            彻底弄懂Python标准库源码（三）—— pprint模块
          
        </div>
      </a>
    
    
      <a href="/2020/08/23/%E5%AE%9E%E7%94%A8%E8%BD%AF%E4%BB%B6%E6%8E%A8%E8%8D%90(%E4%B8%80)%E2%80%94%E2%80%94%E8%87%AA%E5%8A%A8%E6%9B%B4%E6%8D%A2%E5%A3%81%E7%BA%B8%20(Dynamic%20theme)/" class="article-nav-link">
        <strong class="article-nav-caption">下一篇</strong>
        <div class="article-nav-title">实用软件推荐(一)——自动更换壁纸 (Dynamic theme)</div>
      </a>
    
  </nav>

   
<!-- valine评论 -->
<div id="vcomments-box">
  <div id="vcomments"></div>
</div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/valine@1.4.14/dist/Valine.min.js"></script>
<script>
  new Valine({
    el: "#vcomments",
    app_id: "zy6yBRj9KkWO2XxsT94n1DIW-gzGzoHsz",
    app_key: "auroBE2PQXkQ05CLi30SFv92",
    path: window.location.pathname,
    avatar: "monsterid",
    placeholder: "给我的文章加点评论吧~",
    recordIP: true,
  });
  const infoEle = document.querySelector("#vcomments .info");
  if (infoEle && infoEle.childNodes && infoEle.childNodes.length > 0) {
    infoEle.childNodes.forEach(function (item) {
      item.parentNode.removeChild(item);
    });
  }
</script>
<style>
  #vcomments-box {
    padding: 5px 30px;
  }

  @media screen and (max-width: 800px) {
    #vcomments-box {
      padding: 5px 0px;
    }
  }

  #vcomments-box #vcomments {
    background-color: #fff;
  }

  .v .vlist .vcard .vh {
    padding-right: 20px;
  }

  .v .vlist .vcard {
    padding-left: 10px;
  }
</style>

 
   
  
</article>

</section>
      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2020-2021
        <i class="ri-heart-fill heart_icon"></i> 杰克小麻雀
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/favicon.ico" alt="半亩方塘"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/img/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/img/wechat.jpg">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->


<script src="/js/tocbot.min.js"></script>

<script>
  tocbot.init({
    tocSelector: '.tocbot',
    contentSelector: '.article-entry',
    headingSelector: 'h1, h2, h3, h4, h5, h6',
    hasInnerContainers: true,
    scrollSmooth: true,
    scrollContainer: 'main',
    positionFixedSelector: '.tocbot',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto'
  });
</script>

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->


<script src="/js/clickBoom2.js"></script>


<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
  </div>
</body>
<script type="text/javascript">
if(!/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)){
  document.write('<script type="text/javascript" src="/js/FunnyTitle.js"><\/script>');
  document.write('<script type="text/javascript" src="/js/snow.js"><\/script>');
}
</script>
</html>